package com.exercise.module.common.file.service;/**
 * Created by kete on 2017-05-11.
 */

import com.exercise.core.exception.EbsException;
import com.exercise.core.util.EntityUtil;
import com.exercise.core.util.ExecTime;
import com.exercise.module.common.config.service.ConfigService;
import com.exercise.module.common.dao.entity.EbsFile;
import com.exercise.module.common.dao.entity.SysDocFile;
import com.exercise.module.common.dao.mapper.SysDocFileMapper;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.Date;
import java.util.List;

/**
 * 作者：kete
 * 创建时间：2017-05-11 16:26
 * 功能描述：
 * 版本：
 */
@Service
public class FtpFileService extends AbstractFileService implements FileService {
    private static Logger logger = LoggerFactory.getLogger(FtpFileService.class);

    @Autowired
    private ConfigService configService;

    @Autowired
    private SysDocFileMapper sysDocFileMapper;

    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    @Transactional(propagation= Propagation.NOT_SUPPORTED)
    public String putFile(String local, String module1, String module2) {

        File file = new File(local);
        if(file.exists()) {
            return putFile(file,module1,module2);
        }
        return "";
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(File local, String module1, String module2) {

        String fileName = local.getName();

        String fileId = putFile(local,module1,module2,fileName);
        return fileId;
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(MultipartFile local, String module1, String module2) {

        try {
            String checkSum = createChecksum(local.getInputStream());
            return putFile(local.getInputStream(),module1,module2,local.getOriginalFilename(),checkSum);
        } catch (IOException e) {
            throw new EbsException(e);
        }
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(String local, String module1, String module2, String rename) {

        File file = new File(local);
        if(file.exists()) {
            return putFile(file,module1,module2,rename);
        }
        return "";
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(File local, String module1, String module2, String rename) {

        if(!local.exists()) return null;

        try {
            InputStream input = new FileInputStream(local);
            String checkSum = createChecksum(new FileInputStream(local));
            String fileId = putFile(input,module1,module2,rename,checkSum);
            return fileId;
        } catch(Exception e) {
            throw new EbsException(e);
        }
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(InputStream local, String module1, String module2,String rename) {

        return putFile(local, module1, module2,rename,"");
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(MultipartFile local, String module1,String module2, String rename) {
        try {
            String checkSum = createChecksum(local.getInputStream());
            return putFile(local.getInputStream(),module1,module2,rename,checkSum);
        } catch (IOException e) {
            throw new EbsException(e);
        }
    }

    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    public String putFile(InputStream local, String module1, String module2,String rename,String checkSum) {

        try {
            /** 文件排重 */
            if(StringUtils.isNotEmpty(checkSum)) {
                SysDocFile sysDocFileOld = sysDocFileMapper.selectByChecksum(checkSum);
                if(sysDocFileOld != null && StringUtils.isNotEmpty(sysDocFileOld.getId())) {

                    if(!StringUtils.endsWithIgnoreCase(sysDocFileOld.getFileName(), rename) ||
                            !StringUtils.endsWithIgnoreCase(sysDocFileOld.getModule1(), module1) ||
                            !StringUtils.endsWithIgnoreCase(sysDocFileOld.getModule2(), module2)) {
                        sysDocFileOld.setId(getUUID());
                        sysDocFileOld.setFileName(rename);
                        sysDocFileOld.setModule1(module1);
                        sysDocFileOld.setModule2(module2);
                        sysDocFileMapper.insert(sysDocFileOld);
                    }
                    return sysDocFileOld.getId();
                }
            }

            String fileName = rename;
            Integer fileSize = local.available();

            String remoteFilePath = separator;
            String companyCode = getCompanyCode();
            String dateStr = DateFormatUtils.format(new Date(), "yyyyMMdd");
            String fileId = getUUID();
            remoteFilePath += companyCode + separator;
            remoteFilePath += dateStr + separator;
            remoteFilePath += module1 + separator;
            remoteFilePath += module2 + separator;
            remoteFilePath += fileId + "." + FilenameUtils.getExtension(fileName);

            uploadFile(remoteFilePath,local);

            SysDocFile sysDocFile = new SysDocFile();
            EntityUtil.setValue(sysDocFile);
            sysDocFile.setId(fileId);
            sysDocFile.setFileName(fileName);
            sysDocFile.setFilePath(remoteFilePath);
            sysDocFile.setFileSize(fileSize);
            sysDocFile.setFileType(FilenameUtils.getExtension(rename));
            sysDocFile.setModule1(module1);
            sysDocFile.setModule2(module2);
            sysDocFile.setServerNode(configService.getString("ftp_server_node","a"));
            sysDocFile.setFileChecksum(checkSum);
            sysDocFile.setCreateTime(new Date());

            sysDocFileMapper.insert(sysDocFile);
            return fileId;
        } catch(Exception e) {
            throw new EbsException(e);
        }
    }

    public EbsFile getFileById(String fileId) {

        SysDocFile sysDocFile = sysDocFileMapper.selectByPrimaryKey(fileId);

        return getEbsFile(sysDocFile);
    }

    public EbsFile getFileById(String fileId,String accessCode) {

        SysDocFile sysDocFile = sysDocFileMapper.selectByPrimaryKey(fileId);
        if(StringUtils.equals(sysDocFile.getAccessCode(), accessCode)) {
            return getEbsFile(sysDocFile);
        }
        throw new EbsException("提取码错误");
    }


    public EbsFile copyFileById(String fileId, OutputStream out) {

        SysDocFile sysDocFile = sysDocFileMapper.selectByPrimaryKey(fileId);
        return getEbsFile(sysDocFile,out,false);
    }

    public EbsFile getFileByPath(String filePath) {
        SysDocFile sysDocFile = sysDocFileMapper.selectByPath(filePath);
        if(sysDocFile == null) {
            sysDocFile = new SysDocFile();
            sysDocFile.setFilePath(filePath);
            sysDocFile.setFileName(FilenameUtils.getName(filePath));
        }
        return getEbsFile(sysDocFile);
    }

    public EbsFile copyFileByPath(String filePath, OutputStream out) {

        SysDocFile sysDocFile = sysDocFileMapper.selectByPath(filePath);
        return getEbsFile(sysDocFile,out,false);
    }

    public String getFileNameById(String fileId) {

        if(StringUtils.isEmpty(fileId)) return "";

        SysDocFile sysDocFile = sysDocFileMapper.selectByPrimaryKey(fileId);

        if(sysDocFile == null) return "";

        return sysDocFile.getFileName();
    }

    public String getFileNameByPath(String filePath) {

        SysDocFile sysDocFile = sysDocFileMapper.selectByPath(filePath);
        return sysDocFile.getFileName();
    }

    public SysDocFile getSysDocFileById(String fileId) {
        SysDocFile sysDocFile = sysDocFileMapper.selectByPrimaryKey(fileId);
        return sysDocFile;
    }

    public List<SysDocFile> getSysDocFileByIds(String[] ids) {

        if(ids == null || ids.length == 0) return null;

        List<SysDocFile> list = sysDocFileMapper.selectByPrimaryKeys(ids);

        return list;
    }

    public SysDocFile getSysDocFileByPath(String filePath) {
        SysDocFile sysDocFile = sysDocFileMapper.selectByPath(filePath);
        return sysDocFile;

    }

    private EbsFile getEbsFile(SysDocFile sysDocFile) {

        if(sysDocFile == null) return null;

        String filePath = getLocationDir();
        filePath += sysDocFile.getFilePath();

        String dir = FilenameUtils.getFullPath(filePath);
        mkdirs(dir);

        OutputStream out = null;
        try {
            File file = new File(filePath);
            boolean existsFile = sysDocFile.getId() == null?false:existsFile(sysDocFile,file);
            if(!existsFile)
                out = new FileOutputStream(file);

            EbsFile ebsFile = getEbsFile(sysDocFile,out,existsFile);

            saveChecksum(sysDocFile,file);
            return ebsFile;
        } catch(Exception e) {
            throw new EbsException(e);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    private EbsFile getEbsFile(SysDocFile sysDocFile,OutputStream out,boolean existsFile) {

        if(sysDocFile == null) return null;

        String filePath = getLocationDir();
        filePath += sysDocFile.getFilePath();

        try {
            if(!existsFile) {
                downloadFile(sysDocFile.getFilePath(),out,sysDocFile.getServerNode());
//
//                if(StringUtils.isNotEmpty(sysDocFile.getFileChecksum())) {
//                    String cacheKey = configService.getNodeName() + sysDocFile.getFilePath();
//                    CacheManager.set(cacheKey, sysDocFile.getFileChecksum());
//                }
            }
            IOUtils.closeQuietly(out);

            EbsFile ebsFile = new EbsFile();
            File file = new File(filePath);
            ebsFile.setFile(file);
            ebsFile.setFileId(sysDocFile.getId());
            ebsFile.setFileName(sysDocFile.getFileName());
            ebsFile.setFilePath(sysDocFile.getFilePath());
            ebsFile.setOwnerUserId(sysDocFile.getOwnerUserId());
            return ebsFile;
        } catch(Exception e) {
            throw new EbsException(e);
        }
    }

    private boolean existsFile(SysDocFile sysDocFile,File file) {

        if(!file.exists()) return false;

        InputStream input = null;
        try {
            input = new FileInputStream(file);
            int fileSize = input.available();
            if(fileSize <= 0) {
                return false;
            }

//            String cacheKey = configService.getNodeName() + sysDocFile.getFilePath();
//            String checksum = CacheManager.get(cacheKey);
            String checksum = "";
            if(StringUtils.isEmpty(checksum)) {
                checksum = createChecksum(input);
            }

            if(StringUtils.equalsIgnoreCase("bid2", sysDocFile.getFileType())) {
                if(StringUtils.equalsIgnoreCase(sysDocFile.getFileChecksum(), checksum) && fileSize >= sysDocFile.getFileSize()) {
                    return true;
                }
            } else {
                if(StringUtils.equalsIgnoreCase(sysDocFile.getFileChecksum(), checksum) && fileSize == sysDocFile.getFileSize()) {
                    return true;
                }
            }

            return false;
        } catch (Exception e) {
            throw new EbsException(e);
        } finally {
            IOUtils.closeQuietly(input);
        }
    }

    private void saveChecksum(SysDocFile sysDocFile,File file) {

        if(StringUtils.isNotEmpty(sysDocFile.getFileChecksum()) || !file.exists() || sysDocFile.getId() == null) return;

        InputStream input = null;
        try {
            input = new FileInputStream(file);
            int fileSize = input.available();
            if(fileSize <= 0) {
                return ;
            }

            String checksum = createChecksum(input);
            SysDocFile update = new SysDocFile();
            update.setId(sysDocFile.getId());
            update.setFileChecksum(checksum);
            update.setFileSize(fileSize);

            sysDocFileMapper.updateByPrimaryKeySelective(update);
        } catch (Exception e) {
            throw new EbsException(e);
        } finally {
            IOUtils.closeQuietly(input);
        }
    }

    private FTPClient getFtpClient() {

        try {
            FTPClient ftpClient = new FTPClient();
            ftpClient.setDefaultTimeout(10 * 1000);
            ftpClient.setConnectTimeout(10 * 1000);
            ftpClient.setDataTimeout(30 * 1000);
            ftpClient.setDefaultPort(NumberUtils.toInt(configService.getString("ftp_port"),21));
            ftpClient.connect(configService.getString("ftp_host"));
            ftpClient.login(configService.getString("ftp_username"), configService.getString("ftp_password"));
            logger.info("FTP 已连接!");
            return ftpClient;
        } catch (Exception e) {
            throw new  EbsException(e);
        }
    }

    private FTPClient getFtpClient(String serverNode) {

        try {
            String ftpHost = configService.getString("ftp_host_" + serverNode);
            String ftpUsername = configService.getString("ftp_username_" + serverNode);
            String ftpPassword = configService.getString("ftp_password_" + serverNode);
            String ftpPort = configService.getString("ftp_port_" + serverNode);
            if(StringUtils.isEmpty(ftpHost)) {
                ftpHost = configService.getString("ftp_host");
                ftpUsername = configService.getString("ftp_username");
                ftpPassword = configService.getString("ftp_password");
                ftpPort = configService.getString("ftp_port");
            }

            FTPClient ftpClient =new FTPClient();
            ftpClient.setDefaultTimeout(10 * 1000);
            ftpClient.setConnectTimeout(10 * 1000);
            ftpClient.setDataTimeout(30 * 1000);
            ftpClient.setDefaultPort(NumberUtils.toInt(ftpPort,21));
            ftpClient.connect(ftpHost);
            ftpClient.login(ftpUsername, ftpPassword);
            logger.info("FTP 已连接!");
            return ftpClient;
        } catch (Exception e) {
            throw new  EbsException(e);
        }
    }

    private void disconnect(FTPClient ftpClient) {

        try	{
            if(ftpClient != null) {
                ftpClient.logout();
                ftpClient.disconnect();
                ftpClient = null;
                logger.info("FTP 断开连接!");
            }
        } catch(Exception e) {
            logger.error("FTP 断开连接错误!",e);
        }
    }

    private boolean uploadFile(String remoteFilePath, InputStream local) {

        logger.info("FTP上传：存储文件 = " + remoteFilePath);

        while(remoteFilePath.startsWith(separator)) {
            remoteFilePath = StringUtils.substring(remoteFilePath, 1);
        }

        String ftpDefaultDir = configService.getString("ftp_default_dir");

        FTPClient ftpClient = getFtpClient();
        ftpClient.setBufferSize(1024*1024*2);

        String fileName = FilenameUtils.getName(remoteFilePath);
        String fileDir = FilenameUtils.getFullPathNoEndSeparator(remoteFilePath);
        try {
            boolean changeDirOk = ftpClient.changeWorkingDirectory(ftpDefaultDir);

            if(StringUtils.isNotBlank(fileDir)) {
                changeDirOk = ftpClient.changeWorkingDirectory(fileDir);
                if(!changeDirOk) {
                    makeFtpDirs(ftpClient,fileDir);
                }
            }
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            boolean uploadOk = ftpClient.storeFile(fileName, local);
            if(!uploadOk)throw new Exception("上传 " + remoteFilePath + " 失败!");

            return true;
        } catch (Exception e) {
            throw new EbsException(e);
        } finally {
            IOUtils.closeQuietly(local);
            disconnect(ftpClient);
        }
    }

    private void downloadFile(String filePath, OutputStream output,String serverNode) throws Exception {

        ExecTime execTime = new ExecTime();
        logger.info("FTP下载：文件路径 =" + filePath);

        while(filePath.startsWith(separator)) {
            filePath = StringUtils.substring(filePath, 1);
        }

        FTPClient ftpClient = getFtpClient(serverNode);
        String ftpDefaultDir = configService.getString("ftp_default_dir_" + serverNode);
        if(StringUtils.isEmpty(ftpDefaultDir))
            ftpDefaultDir = configService.getString("ftp_default_dir");
        try {
            boolean changeDirOk = ftpClient.changeWorkingDirectory(ftpDefaultDir);
            boolean downloadOk = false;
            if(changeDirOk) {
                ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
                downloadOk = ftpClient.retrieveFile(filePath, output);
            }
            if(!downloadOk)throw new Exception("下载 " + filePath + " 失败!");
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            logger.info("下载文件：" + filePath + " 耗时："+ execTime);
        }
    }

    private void makeFtpDirs(FTPClient ftpClient,String dirName){

        File dir = new File(dirName);
        String currentDirName = dir.getName();
        if(dir.getParent() != null) {
            makeFtpDir(ftpClient,dir.getParent());
        }
        makeFtpDir(ftpClient,currentDirName);
    }

    private void makeFtpDir(FTPClient ftpClient,String dirName) {

        try {
            File dir = new File(dirName);
            if(dir.getParent() != null) {
                makeFtpDir(ftpClient,dir.getParent());
            }

            String currentDirName = dir.getName();
            boolean changeDirOk = ftpClient.changeWorkingDirectory(currentDirName);
            if(!changeDirOk) {
                ftpClient.makeDirectory(currentDirName);
                changeDirOk = ftpClient.changeWorkingDirectory(currentDirName);
            }
            if(!changeDirOk)throw new Exception("创建目录" + dirName + "失败！");
        } catch (Exception e) {
            throw new EbsException(e);
        }
    }

}
